﻿// Decompiled with JetBrains decompiler
// Type: Netick.PlayersReplicator
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

internal sealed class PlayersReplicator : ReplicatorComponent
{
  private int _maxPlayers;
  private HashSet<NetworkPlayerId> _prePlayers;
  private List<NetworkPlayerId> _newPlayers;
  private List<NetworkPlayerId> _leftPlayers;
  private List<NetworkPlayerId> _connectedPlayers;
  private readonly int ItemSizeWords;

  public IReadOnlyList<NetworkPlayerId> ConnectedPlayers
  {
    get => (IReadOnlyList<NetworkPlayerId>) this._connectedPlayers;
  }

  public unsafe PlayerData this[int index]
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)] get
    {
      if (index < 0 || index > this._maxPlayers)
        throw new IndexOutOfRangeException();
      return ((PlayerData*) this.StatePtr)[index];
    }
    set
    {
      if (index < 0 || index > this._maxPlayers)
        throw new IndexOutOfRangeException();
      if (this.Engine.IsClient)
        throw new NotServerException();
      int* numPtr = (int*) &value;
      int* statePtr = this.StatePtr;
      int num = index * this.ItemSizeWords;
      for (int index1 = 0; index1 < this.ItemSizeWords; ++index1)
      {
        int* p = statePtr + num + index1;
        if (numPtr[index1] != *p)
          this.MarkAltered(p);
        *p = numPtr[index1];
      }
      ((PlayerData*) this.StatePtr)[index] = value;
    }
  }

  public PlayersReplicator(NetickEngine engine)
    : base(engine)
  {
    this.ItemSizeWords = NetickUtils.GetWordSize(sizeof (PlayerData));
    this._maxPlayers = engine.MaxPlayers + 1;
    this._prePlayers = new HashSet<NetworkPlayerId>(this._maxPlayers);
    this._newPlayers = new List<NetworkPlayerId>(this._maxPlayers);
    this._leftPlayers = new List<NetworkPlayerId>(this._maxPlayers);
    this._connectedPlayers = new List<NetworkPlayerId>(this._maxPlayers);
  }

  public override int GetSizeWords()
  {
    return this._maxPlayers * NetickUtils.GetWordSize(sizeof (PlayerData));
  }

  public override unsafe void Reset()
  {
    this._newPlayers.Clear();
    this._leftPlayers.Clear();
    this._connectedPlayers.Clear();
    this._prePlayers.Clear();
    for (int index = 0; index < this._maxPlayers; ++index)
      *((PlayerData*) this.StatePtr + index) = new PlayerData()
      {
        ObjectId = -1,
        Version = 0
      };
  }

  public void ConnectHostPlayer()
  {
    if (this.Engine.StartMode != NetickStartMode.Host && this.Engine.StartMode != NetickStartMode.SinglePlayer || this._connectedPlayers.Contains(this.Engine.LocalPeer.PlayerId))
      return;
    this.OnPlayerConnected((NetworkPlayer) this.Engine.LocalPeer);
  }

  public override void PostReadPhase1()
  {
    this._connectedPlayers.Clear();
    for (int index = 0; index < this._maxPlayers; ++index)
    {
      NetworkPlayerId playerId = this[index].GetPlayerId(index);
      if (playerId.IsValid)
        this._connectedPlayers.Add(playerId);
    }
  }

  public void SetPlayerEntity(NetworkPlayerId player, Entity entity)
  {
    if (!player.IsValid)
      return;
    if (this.Engine.IsClient)
      throw new NotServerException();
    int id = player.Id;
    this[id] = new PlayerData(entity != null ? entity.RawId : -1, this[id].Version);
  }

  public Entity GetPlayerEntity(NetworkPlayerId player)
  {
    if (!player.IsValid)
      return (Entity) null;
    PlayerData playerData = this[player.Id];
    if (playerData.Version <= 0)
      return (Entity) null;
    Entity entity;
    return this.Engine.AllEntities.TryGetValue(playerData.ObjectId, out entity) ? entity : (Entity) null;
  }

  public void OnPlayerConnected(NetworkPlayer player) => this._newPlayers.Add(player.PlayerId);

  public void OnPlayerDisconnected(NetworkPlayer player) => this._leftPlayers.Add(player.PlayerId);

  public bool ContainsPlayer(NetworkPlayerId playerId) => this[playerId.Id].Version > 0;

  public void DispatchCallbacks()
  {
    if (this.Engine.IsClient)
    {
      this._newPlayers.Clear();
      this._leftPlayers.Clear();
      for (int index = 0; index < this._maxPlayers; ++index)
      {
        NetworkPlayerId playerId = this[index].GetPlayerId(index);
        if (playerId.IsValid)
        {
          if (!this._prePlayers.Contains(playerId))
            this._newPlayers.Add(playerId);
          else
            this._prePlayers.Remove(playerId);
        }
      }
      foreach (NetworkPlayerId prePlayer in this._prePlayers)
        this._leftPlayers.Add(prePlayer);
      this._prePlayers.Clear();
      for (int index = 0; index < this._maxPlayers; ++index)
      {
        NetworkPlayerId playerId = this[index].GetPlayerId(index);
        if (playerId.IsValid)
          this._prePlayers.Add(playerId);
      }
    }
    if (this.Engine.IsServer)
    {
      foreach (NetworkPlayerId leftPlayer in this._leftPlayers)
        this._connectedPlayers.RemoveUnordered(leftPlayer);
    }
    foreach (NetworkPlayerId leftPlayer in this._leftPlayers)
      this.Engine._gameEngine.OnPlayerLeft(leftPlayer);
    if (this.Engine.IsServer)
    {
      foreach (NetworkPlayerId newPlayer in this._newPlayers)
        this._connectedPlayers.Add(newPlayer);
    }
    if (this.Engine.IsServer)
    {
      foreach (NetworkPlayerId leftPlayer in this._leftPlayers)
        this.HandlePlayerEvent(leftPlayer, false);
      foreach (NetworkPlayerId newPlayer in this._newPlayers)
        this.HandlePlayerEvent(newPlayer, true);
    }
    foreach (NetworkPlayerId newPlayer in this._newPlayers)
      this.Engine._gameEngine.OnPlayerJoined(newPlayer);
    this._leftPlayers.Clear();
    this._newPlayers.Clear();
  }

  public void HandlePlayerEvent(NetworkPlayerId player, bool isConnectEvent)
  {
    int id = player.Id;
    int version = this[id].Version;
    if (isConnectEvent)
    {
      if (version <= 0)
        version = version * -1 + 1;
    }
    else if (version > 0)
      version *= -1;
    this[id] = new PlayerData(-1, version);
  }
}
